package com.ys.erp.gateway.config.exception;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.ys.erp.framework.response.AbstractResponseCode;
import com.ys.erp.framework.response.ResponseDTO;
import jakarta.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * @author lwj
 * @date 2024/8/30
 */
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
public class JsonExceptionHandler implements ErrorWebExceptionHandler {

    private static final Map<String, Function<Throwable, ResponseDTO<?>>> exceptionMap = new ConcurrentHashMap<>();

    static {
        //项目异常
        exceptionMap.put("java.lang.RuntimeException", (e) -> {
            ResponseDTO<?> responseErrorDTO = ResponseDTO.createByError(-1, e.getMessage(), null);
            log.warn("自定义业务异常处理：{} \n 返回参数 {}", e.getMessage(), responseErrorDTO);
            return responseErrorDTO;
        });
    }

    @Override
    @Nonnull
    public Mono<Void> handle(@Nonnull ServerWebExchange exchange, @Nonnull Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        if (response.isCommitted()) {
            //对于已经committed(提交)的response，就不能再使用这个response向缓冲区写任何东西
            return Mono.error(ex);
        }
        log.error("异常：", ex);
        // header set 响应JSON类型数据，统一响应数据结构（适用于前后端分离JSON数据交换系统）
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 按照异常类型进行翻译处理
        String name = ex.getClass().getName();
        ResponseDTO<?> responseDTO;
        if (exceptionMap.containsKey(name)) {
            responseDTO = exceptionMap.get(name).apply(ex);
        } else {
            responseDTO = AbstractResponseCode.ERROR;
        }
        //响应
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(responseDTO, JSONWriter.Feature.WriteMapNullValue));
        }));
    }
}
